import wx
import weakref

from clipimage import applyMask, boundingBox, generateMask

from math import sin, cos



class BodyPart(object):

    def __init__(self, baseImage, extractPolygon=None, center=None,
                 mirrored=False, angle=0.0, zoom=None):
        self.baseImage = baseImage
        self.extractPolygon = extractPolygon
        self.center = center
        self.mirrored = mirrored
        self.angle = angle
        self.zoom = zoom
        
        self.cachedBitmap = None


    def setBaseImage(self, baseImage):
        self.baseImage = baseImage
        self.imageCache = None


    def getBaseImage(self):
        return self.baseImage


    def setExtractPolygon(self, extractPolygon):
        self.extractPolygon = extractPolygon
        self.imageCache = None


    def getExtractPolygon(self):
        return self.getExtractPolygon


    def getCachedBitmap(self):
        if self.cachedBitmap != None:
            return self.cachedBitmap
        else:
            if self.extractPolygon == None:
                self.cachedBitmap = wx.BitmapFromImage(self.baseImage)
            else:
                x1,y1,x2,y2 = boundingBox(self.extractPolygon)
                mask = generateMask(self.extractPolygon, x1,y1,x2,y2)
                img = self.baseImage.GetSubImage(wx.Rect(x1, y1, x2-x1, y2-y1))
                self.cachedBitmap = applyMask(img, mask)
            return self.cachedBitmap


    def setCenter(self, center):
        self.center = center


    def getCenter(self):
        if self.center == None:
            return (self.getCachedBitmap().GetWidth() / 2.0,
                    self.getCachedBitmap().GetHeight() / 2.0 )
        else:
            return self.center


    def setMirrored(self, mirrored):
        self.mirrored = mirrored


    def getMirrored(self):
        return self.mirrored


    def setAngle(self, angle):
        self.angle = angle


    def getAngle(self):
        return self.angle


    def setZoom(self, zoom):
        self.zoom = zoom


    def getZoom(self):
        if self.zoom == None:
            return min(1.0/self.getCachedBitmap().GetWidth(),
                       1.0/self.getCachedBitmap().GetHeight())
        else:
            return self.zoom


class Body(object):

    def __init__(self, bodyPart=None, children=[],
                 pos=(0.0,0.0), speed=(0.0,0.0),
                 angle=0.0, angleSpeed=0.0,
                 zoom=1.0):
        
        self.bodyPart = bodyPart
        self.parent = None
        self.children = []
        for child in children:
            self.insertChild(child)
        self.fixedByChild = None
        self.drawBehindChild = None
        
        self.pos = pos
        self.speed = speed
        self.angle = angle
        self.angleSpeed=angleSpeed
        self.zoom = zoom
        
        self.cachedAbsPos = None
        self.cachedAbsSpeed = None
        self.cachedAbsAngle = None
        self.cachedAbsAngleSpeed = None
        self.cachedAbsZoom = None


    def setBodyPart(self, bodyPart):
        self.bodyPart = bodyPart


    def getBodyPart(self):
        return self.bodyPart


    def insertChild(self, child, insertBehindChild=None):
        if insertBehindChild == None:
            self.children.append(child)
        else:
            self.children.insert(self.children.index(insertBehindChild), child)
        child.parent = weakref.proxy(self)


    def removeChild(self, child):
        ndx = self.children.index(child)
        self.children[ndx].parent = None
        del self.children[ndx]
        if self.fixedByChild == child:
            self.fixedByChild = None
        if self.drawBehindChild == child:
            if ndx == 0:
                self.drawBehindChild = None
            else:
                self.drawBehindChild = self.children[ndx-1]


    def iterChildrenBot2Top(self, skipBefore=False, skipBehind=False):
        isBehind = True
        for child in self.children:
            if child == self.drawBehindChild: isBehind = False
            if not skipBehind and isBehind: yield child
            if not skipBefore and not isBehind: yield child


    def iterChildrenTop2Bot(self, skipBefore=False, skipBehind=False):
        isBehind = (self.drawBehindChild == None)
        for child in self.children.reversed():
            if not skipBehind and isBehind: yield child
            if not skipBefore and not isBehind: yield child
            if child == self.drawBehindChild: isBehind = True


    def __iter__(self):
        return self.iterChildrenTop2Bot()


    def getDrawBehindChild(self):
        return self.drawBehindChild


    def setDrawBehindChild(self, drawBehindChild):
        self.drawBehindChild = drawBehindChild


    def setPos(self, pos):
        if self.pos == pos: return
        self.pos = pos
        self.flushCaches()


    def getPos(self):
        return self.pos


    def setSpeed(self, speed):
        if self.speed == speed: return
        self.speed = speed
        self.flushCaches()


    def getSpeed(self):
        return self.speed


    def setAngle(self, angle):
        if self.angle == angle: return
        self.angle = angle
        self.flushCaches()


    def getAngle(self):
        return self.angle


    def setZoom(self, zoom):
        if self.zoom == zoom: return
        self.zoom = zoom
        self.flushCaches()


    def flushCaches(self):
        self.cachedAbsPos = None
        self.cachedAbsSpeed = None
        self.cachedAbsAngle = None
        self.cachedAbsZoom = None
        for child in self.children:
            child.flushCaches()

        
    def getAbsPos(self):
        if self.cachedAbsPos:
            return self.cachedAbsPos
        if not self.parent:
            return self.pos
        parentX, parentY = self.parent.getAbsPos()
        a = self.parent.getAbsAngle()
        x, y = self.pos
        self.cachedAbsPos = ( parentX + x*cos(a) - y*sin(a),
                              parentY + x*sin(a) + y*cos(a) )
        return self.cachedAbsPos


    def getAbsSpeed(self):
        if self.cachedAbsSpeed:
            return self.cachedAbsSpeed
        if not self.parent:
            return self.speed
        parentSpeedX, parentSpeedY = self.parent.getAbsSpeed()
        a = self.parent.getAbsAngle()
        xSpeed, ySpeed = self.speed
        self.cachedAbsSpeed = ( parentSpeedX + xSpeed*cos(a) - ySpeed*sin(a),
                                parentSpeedY + xSpeed*sin(a) + ySpeed*cos(a) )
        return self.cachedAbsSpeed


    def getAbsAngle(self):
        if self.cachedAbsAngle:
            return self.cachedAbsAngle
        if not self.parent:
            return self.angle
        parentAngle = self.parent.getAbsAngle()
        self.cachedAbsAngle = parentAngle+self.angle
        return self.cachedAbsAngle


    def getAbsAngleSpeed(self):
        if self.cachedAbsAngleSpeed:
            return self.cachedAbsAngleSpeed
        if not self.parent:
            return self.angleSpeed
        parentAngleSpeed = self.parent.getAbsAngleSpeed()
        self.cachedAbsAngleSpeed = parentAngleSpeed+self.angleSpeed
        return self.cachedAbsAngleSpeed


    def getAbsZoom(self):
        if self.cachedAbsZoom:
            return self.cachedAbsZoom
        if not self.parent:
            return self.zoom
        parentZoom = self.parent.getAbsZoom()
        self.cachedAbsZoom = parentZoom*self.zoom
        return self.cachedAbsZoom
